This notebook will probably make more sense after reading "Building the static visualization.ipynb"


In [1]:
import pandas as pd
from numpy import where
from bokeh.plotting import show, output_notebook
output_notebook()


BokehJS successfully loaded.

In [2]:
from chart_constants import WATER_COLOR_RANGE, SANITATION_COLOR_RANGE, GRAY, ORANGE

def color_data(data):                                                           
    def _get_color(value, palette):                                             
        if value < 0:                                                           
            return GRAY                                                         
        index = int((value-0.01) / 10)                                          
        return palette[index]                                                   
                                                                                
    data['wat_color'] = data['wat_value'].apply(                                
        _get_color, args=([WATER_COLOR_RANGE])                                  
    )                                                                           
    data['san_color'] = data['san_value'].apply(                                
        _get_color, args=([SANITATION_COLOR_RANGE])                             
    )                                                                           
    data['wat_value'] = where(data['wat_value'] < 0, '-', data['wat_value'])    
    data['san_value'] = where(data['san_value'] < 0, '-', data['san_value'])    
    return data

Get the data


In [3]:
wat_df = pd.read_hdf('water_data_all_years.hdf', 'df')
san_df = pd.read_hdf('san_data_all_years.hdf', 'df')

In [4]:
data = pd.read_hdf('static_map_data.hdf', 'df')  # note we have to use hdf, not csv for the xs & ys lists
data.head()


Out[4]:
name xs ys country_id wat_value san_value wat_color san_color year
0 Algeria [11.9995056495, 8.57289310063, 5.67756595218, ... [23.4716684026, 21.5656607122, 19.6012069768, ... 36 - - #CCCCCC #CCCCCC 1990
1 Angola [16.3265283546, 16.5731799659, 16.8601908708, ... [-5.87747039147, -6.62264454512, -7.2222978654... 18 - - #CCCCCC #CCCCCC 1990
2 Benin [2.69170169436, 1.86524051271, 1.61895063641, ... [6.25881724693, 6.14215770103, 6.83203807213, ... 48 57.1 5.0 #62abc7 #d45500 1990
3 Botswana [25.6491634458, 25.8503914731, 26.1647908872, ... [-18.5360258928, -18.7144129371, -19.293085625... 42 91.9 38.6 #3fc0f0 #eb941f 1990
4 Burkina Faso [-2.82749630371, -3.51189897299, -3.9804491845... [9.64246084232, 9.90032623946, 9.86234406172, ... 1 43.6 7.7 #6aa6bd #d45500 1990

In [5]:
def update_data_for_new_year(data, year):
    data = data.drop(['wat_value', 'san_value', 'wat_color', 'san_color'], axis=1)
    data.year = year
    data = data.merge(wat_df, how='left')
    data = data.merge(san_df, how='left')
    data = data.fillna(-99)

    colored_df = color_data(data)
    return colored_df

In [6]:
data = update_data_for_new_year(data, 1991)
data.head()


Out[6]:
name xs ys country_id year wat_value san_value wat_color san_color
0 Algeria [11.9995056495, 8.57289310063, 5.67756595218, ... [23.4716684026, 21.5656607122, 19.6012069768, ... 36 1991 94.0 - #3fc0f0 #CCCCCC
1 Angola [16.3265283546, 16.5731799659, 16.8601908708, ... [-5.87747039147, -6.62264454512, -7.2222978654... 18 1991 42.4 - #6aa6bd #CCCCCC
2 Benin [2.69170169436, 1.86524051271, 1.61895063641, ... [6.25881724693, 6.14215770103, 6.83203807213, ... 48 1991 58.1 5.4 #62abc7 #d45500
3 Botswana [25.6491634458, 25.8503914731, 26.1647908872, ... [-18.5360258928, -18.7144129371, -19.293085625... 42 1991 92.5 40.8 #3fc0f0 #ebb01f
4 Burkina Faso [-2.82749630371, -3.51189897299, -3.9804491845... [9.64246084232, 9.90032623946, 9.86234406172, ... 1 1991 43.7 7.8 #6aa6bd #d45500

Making a map


In [7]:
from bokeh.models import Range1d, Plot
from chart_constants import PLOT_FORMATS

def setup_map_plot():
    x_start, x_end = (-20, 60)
    y_start, y_end = (-40, 40)
    aspect_ratio = (x_end - x_start) / (y_end - y_start)
    x_range = Range1d(x_start, x_end)
    y_range = Range1d(y_start, y_end)
    plot_height = 400    
    return Plot(x_range=x_range, y_range=y_range, title="", plot_width=int(plot_height * aspect_ratio), plot_height=plot_height, **PLOT_FORMATS)

In [8]:
from bokeh.models import Patches, TapTool, HoverTool

def make_map(source, fill_color_string='wat_color', value_string='wat_value', selected_color=ORANGE):
    
    countries = Patches(
        xs='xs', 
        ys='ys',
        fill_color=fill_color_string, 
        line_color="#FFFFFF", 
    )
    selected_countries = Patches(
        xs='xs', 
        ys='ys',
        fill_color=fill_color_string, 
        line_color=selected_color, 
        line_width=5,
    )
    map_box = setup_map_plot()
    map_box.add_glyph(source, countries, selection_glyph=selected_countries, nonselection_glyph=countries)
    map_box.add_tools(TapTool())
    tooltips = "@year<br />@name<br />@" + value_string + " %"
    map_box.add_tools(HoverTool(tooltips=tooltips))
    return map_box

In [9]:
from bokeh.models import Text, Triangle, Rect
from chart_constants import FONT_PROPS_SM, FONT_PROPS_MD, FONT_PROPS_LG, ORANGE, ORANGE_SHADOW

def construct_text_box(source, value_string, color_string, bar_color): 
    # Plot and axes                                                             
    xdr = Range1d(0, 220)                                                       
    ydr = Range1d(0, 120)                                                       
                                                                                
    plot = Plot(                                                                
        x_range=xdr,                                                            
        y_range=ydr,                                                            
        title="",                                                               
        plot_width=250,                                                         
        plot_height=120,                                                        
        min_border=0,                                                           
        **PLOT_FORMATS                                                          
    )                                                                           
    # Add the writing                                                           
    country = Text(x=5, y=50, text='name', **FONT_PROPS_MD)                     
    percent = Text(x=15, y=10, text=value_string, text_color=color_string, **FONT_PROPS_LG)  # nopep8
    percent_sign = Text(x=69, y=10, text=['%'], text_color=color_string, **FONT_PROPS_LG)  # nopep8
    line_one = Text(x=90, y=28, text=['of people had'], **FONT_PROPS_SM)        
    line_two_p1 = Text(x=90, y=14, text=['access in'], **FONT_PROPS_SM)         
    line_two_p2 = Text(x=136, y=14, text='year', **FONT_PROPS_SM)               
    plot.add_glyph(source, Text(), selection_glyph=country)                     
    plot.add_glyph(source, Text(), selection_glyph=percent)                     
    plot.add_glyph(source, Text(), selection_glyph=percent_sign)                
    plot.add_glyph(line_one)                                                    
    plot.add_glyph(line_two_p1)                                                 
    plot.add_glyph(source, Text(), selection_glyph=line_two_p2)                 
                                                                                
    # Add the orange box with year                                              
    shadow = Triangle(x=150, y=109, size=25, fill_color=ORANGE_SHADOW, line_color=None)  # nopep8
    plot.add_glyph(shadow)                                                      
    # Add the blue bar                                                          
    rect = Rect(x=75, y=99, width=150, height=5, fill_color=bar_color, line_color=None)  # nopep8
    plot.add_glyph(rect)                                                        
    box = Rect(x=200, y=100, width=100, height=40, fill_color=ORANGE, line_color=None)  # nopep8
    plot.add_glyph(box)                                                         
    year = Text(x=160, y=85, text='year', text_font_size='18pt', text_color="#FFFFF", text_font_style="bold")  # nopep8
    plot.add_glyph(source, Text(), selection_glyph=year)                        
                                                                                
    return plot

In [10]:
from bokeh.plotting import hplot, vplot
from bokeh.models import ColumnDataSource
from bokeh.models.widgets import Tabs, Panel

from chart_constants import GREEN, BLUE, DARK_GRAY


source = ColumnDataSource(data)
source.selected = [30]

water_plot = make_map(source, 'wat_color', 'wat_value', ORANGE)
water_text = construct_text_box(source, 'wat_value', 'wat_color', BLUE)
sanitation_plot = make_map(source, 'san_color', 'san_value', DARK_GRAY)
sanitation_text = construct_text_box(source, 'san_value', 'san_color', GREEN)


tabs = Tabs(tabs=[
        Panel(title="Water", child=hplot(vplot(water_plot), vplot(water_text))),
        Panel(title="Sanitation", child=hplot(vplot(sanitation_plot), vplot(sanitation_text)))
    ])

show(vplot(tabs))



In [11]:
def update(year=1990):
    data = pd.DataFrame(source.data)
    new_data = update_data_for_new_year(data, year)
    source.data['year'] = new_data.year
    source.data['wat_value'] = new_data.wat_value
    source.data['san_value'] = new_data.san_value
    source.data['wat_color'] = new_data.wat_color
    source.data['san_color'] = new_data.san_color
    source.push_notebook()

In [12]:
from IPython.html.widgets import interact
interact(update, year=(1990,2012))



In [ ]: